import {
  Component,
  Input,
  QueryList,
  EventEmitter,
  ChangeDetectionStrategy,
  ViewEncapsulation,
  ContentChildren,
  Output,
  ElementRef,
  ChangeDetectorRef,
  SimpleChanges,
  OnChanges,
  AfterContentInit,
  AfterViewInit,
  Renderer2,
  OnDestroy,
  AfterViewChecked,
} from '@angular/core';
import { merge, Subject } from 'rxjs';
import { ResizeObserverObject, ResizeObserverService } from '../core/api';
import { TypeUtils } from '../core/utils/api';
import { WizardStepComponent } from './wizard-step.component';

@Component({
  selector: 'lv-wizard, [lv-wizard]',
  exportAs: 'lvWizard',
  templateUrl: './wizard.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  host: {
    '[class.lv-wizard]': 'true',
    '[class.lv-wizard-horizontal-label]': 'lvLabelPlacement === "horizontal"',
    '[class.lv-wizard-theme-dark]': 'lvTheme === "dark"',
    '[class.lv-wizard-theme-light]': 'lvTheme === "light"',
    '[class.lv-wizard-can-click]': 'lvClick?.observers?.length',
    '[class.lv-wizard-vertical]': 'lvDirection === "vertical"',
  },
})
export class WizardComponent implements OnChanges, AfterContentInit, AfterViewInit, OnDestroy, AfterViewChecked {
  @Input() lvActiveIndex: number = 0;
  @Input() lvStartNumber: number = 1;

  /** @deprecated: 该属性废弃 */
  @Input() lvSize: string = 'default'; // 当前无尺寸规范，保留原有API

  @Input() lvLabelWidth: string = 'auto';

  @Input() lvTheme: 'light' | 'dark' = 'light';

  @Input() lvMode: 'number' | 'icon' = 'number';

  @Input() lvType: 'default' | 'dot' = 'default';

  @Input() lvDirection: 'horizontal' | 'vertical' = 'horizontal';

  @Output() lvClick: EventEmitter<any> = new EventEmitter<any>();

  @Input() lvLabelPlacement: 'vertical' | 'horizontal' = 'vertical';

  @Input() lvStatus: 'wait' | 'process' | 'finish' | 'error' = null;

  @ContentChildren(WizardStepComponent) steps: QueryList<WizardStepComponent>;

  TypeUtils = TypeUtils;

  public typeUtils: TypeUtils = TypeUtils;

  public lvStepLabelWidth: string = 'auto';

  public lastStepWidth: string = this.lvLabelWidth;

  public lvLabelMaxWidth: string = this.lvLabelWidth;

  private resizeObserverObject: ResizeObserverObject;

  constructor(
    private cdr: ChangeDetectorRef,
    private elementRef: ElementRef,
    private render: Renderer2,
    private resizeObserverService: ResizeObserverService,
  ) {}

  ngOnDestroy(): void {
    this.resizeObserverObject.unobserve();
  }

  isClickEvent(): boolean {
    return Array.from(this.lvClick.observers).length > 0;
  }

  stepClick(index, step): void {
    if (step.lvReadonly) {
      return;
    }
    this.lvClick.emit(index);
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.lvActiveIndex || changes.lvStatus) {
      this.activeChange(this.lvActiveIndex);
    }
  }

  activeChange(activeIndex): void {
    let index = activeIndex;
    if (this.steps) {
      if (index > this.steps.length - 1) {
        index = this.steps.length - 1;
      }
      this.stepChange(index);
    }
  }

  ngAfterContentInit(): void {
    this.activeChange(this.lvActiveIndex);
    const stepChange = this.steps.map((step): Subject<any> => step.attrChange$);
    merge(...stepChange, this.steps.changes).subscribe((): void => {
      setTimeout((): void => {
        this.activeChange(this.lvActiveIndex);
        this.cdr.detectChanges();
      }, 0);
    });
  }

  ngAfterViewChecked(): void {
    const stepLabels = (this.elementRef.nativeElement as HTMLElement).querySelectorAll('.lv-wizard-step-label');
    if (stepLabels[stepLabels.length - 1]?.getAttribute('style')?.includes('font-weight')) {
      setTimeout((): void => {
        this.setStepLabelWidth(stepLabels);
        this.render.removeStyle(stepLabels[stepLabels.length - 1], 'font-weight');
      });
    }
  }

  ngAfterViewInit(): void {
    const stepLabels = (this.elementRef.nativeElement as HTMLElement).querySelectorAll('.lv-wizard-step-label');
    const render: () => void = (): void => {
      this.lvStepLabelWidth = 'auto';
      // max-width 不能用auto赋值
      this.lvLabelMaxWidth = 'max-content';
      this.lastStepWidth = 'max-content';
      this.render.setStyle(stepLabels[stepLabels.length - 1], 'font-weight', '700');
      this.cdr.detectChanges();
    };
    render();
    this.resizeObserverObject = this.resizeObserverService.observe(this.elementRef.nativeElement, (): void => {
      render();
    });
  }

  stepChange(activeIndex): void {
    const stepsArray = this.steps.toArray();
    this.steps.forEach((step, index): void => {
      if (index < activeIndex) {
        if (TypeUtils.isNotNil(step.lvStatus)) {
          step.status = step.lvStatus;
        } else {
          step.status = 'finish';
        }
      }
      if (index === activeIndex) {
        // 优先使用步骤的状态
        if (TypeUtils.isNotNil(step.lvStatus)) {
          step.status = step.lvStatus;
        } else if (TypeUtils.isNotNil(this.lvStatus)) {
          step.status = this.lvStatus;
        } else {
          step.status = 'process';
        }
      }
      if (index > activeIndex) {
        if (TypeUtils.isNotNil(step.lvStatus)) {
          step.status = step.lvStatus;
        } else {
          step.status = 'wait';
        }
      }

      if (index > 0) {
        const prevStep = stepsArray[index - 1];
        prevStep.nextStatus = step.status;
      }
    });
    this.cdr.markForCheck();
  }

  setStepLabelWidth(stepLabels: any): void {
    const minMargin: number = 32;
    const iconWidth: number = 20;
    const iconMargin: number = 8;
    const stepNum: number = this.steps.length;
    const wizardWidth: number = Number(
      window.getComputedStyle(this.elementRef.nativeElement).getPropertyValue('width').slice(0, -2),
    );
    const lastStepWidth: number = Number(
      window
        .getComputedStyle(stepLabels[stepLabels.length - 1])
        .getPropertyValue('width')
        .slice(0, -2),
    );
    this.lastStepWidth = `${this.lvLabelPlacement === 'vertical' ? lastStepWidth : lastStepWidth - iconWidth - iconMargin}px`;

    const tempWidth: number = (Number(wizardWidth) - Number(lastStepWidth)) / (stepNum - 1);
    this.lvStepLabelWidth = `${tempWidth}px`;
    if (this.lvDirection !== 'vertical') {
      this.lvLabelMaxWidth =
        this.lvLabelPlacement === 'vertical'
          ? `${tempWidth - minMargin}px`
          : `${tempWidth - minMargin - iconWidth - iconMargin - minMargin / 2}px`;
    }
    this.cdr.markForCheck();
  }
}
